class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  [Symbol.iterator]() {
    let nextValue = Math.ceil(this.start);
    const last = this.end;
    return {
      next() {
        return nextValue > last ? { done: true } : { value: nextValue++ };
      },
    };
  }
}

console.log([...new Range(-1.3, 3.5)]); // [ -1, 0, 1, 2, 3 ]
for (const v of new Range(0.2, 3)) console.log(v); // 1 2 3

function map(iterable, f) {
  const iterator = iterable[Symbol.iterator]();
  return {
    [Symbol.iterator]() {
      return {
        next() {
          const result = iterator.next();
          return result.done ? result : { value: f(result.value) };
        },
      };
    },
  };
}
console.log([...map(new Range(1, 4), (x) => x * x)]); // [ 1, 4, 9, 16 ]

function filter(iterable, predicate) {
  const iterator = iterable[Symbol.iterator]();
  return {
    [Symbol.iterator]() {
      return {
        next() {
          let result = iterator.next();
          while (!result.done) {
            if (predicate(result.value)) return result;
            result = iterator.next();
          }
          return result;
        },
      };
    },
  };
}
console.log([...filter(new Range(1, 10), (x) => x % 2 === 0)]); // [ 2, 4, 6, 8, 10 ]
